﻿using PowerTriggers.Properties;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using PowerTriggers.ServiceReference;
using System.Xml.Linq;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceProcess;

namespace PowerTriggers
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "frm"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "frm")]
    public partial class frmMain : Form
    {
        private ProgramContext mainProgram;
        private ConfigData data;
        private bool forceNextActivateToAttemptConnection;
        private bool isFirstRun;
        private string shownServerName = null;
        private int prevHeight;
        private object lockObject = new object();

        public frmMain()
        {
            this.InitializeComponent();
        }

        public frmMain(ProgramContext theMainProgram, bool firstRun)
            : this()
        {
            this.mainProgram = theMainProgram;
            this.isFirstRun = firstRun;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void ShowServerData()
        {
            if (IsConnectedToService(false))
            {
                lock (lockObject)
                {
                    this.txtSuspend.Text = string.Join(", ", (from task in data.SuspendTasks select task.Description + ":" + task.GetActionProperty().SelectedChoice.ToString()).ToArray());
                    this.txtResume.Text = string.Join(", ", (from task in data.ResumeTasks select task.Description + ":" + task.GetActionProperty().SelectedChoice.ToString()).ToArray());
                }
            }

            // show the name of the computer connected to
            if ((shownServerName ?? string.Empty).ToLower() != (WcfClientFactory.CurrentServerName ?? string.Empty).ToLower())
            {
                shownServerName = WcfClientFactory.CurrentServerName;
                if (shownServerName == null || shownServerName.Length == 0)
                {
                    int diff = labelServerName.Bottom - treeViewEventHistory.Bottom;

                    treeViewEventHistory.Height = prevHeight;
                    prevHeight = 0;

                    labelServerName.Visible = false;
                }
                else
                {
                    labelServerName.Text = "Connected to Computer: " + (shownServerName ?? string.Empty);
                    if (prevHeight == 0)
                    {
                        labelServerName.Top = this.ClientSize.Height - labelServerName.Height - 1;

                        prevHeight = treeViewEventHistory.Height;

                        int diff = labelServerName.Bottom - treeViewEventHistory.Bottom + 4;

                        treeViewEventHistory.Height -= diff;
                    }

                    labelServerName.Left = treeViewEventHistory.Left;
                    labelServerName.Width = treeViewEventHistory.Width;

                    labelServerName.Visible = true;
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private bool SelectTasks(string testDescription, ref Task[] tasks, Dictionary<TaskTypes, IList<TaskProperty>> defaultProperties)
        {
            try
            {
                bool success;
                using (SelectTasksForm form = new SelectTasksForm(tasks, defaultProperties, testDescription))
                {
                    var result = form.ShowDialog(this);
                    if (result == System.Windows.Forms.DialogResult.OK)
                    {
                        tasks = form.SelectedTasks.ToArray();
                        success = true;
                    }
                    else
                    {
                        success = false;
                    }
                }
                return success;
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
                return false;
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "data")]
        private void SaveData()
        {
            ConfigData localData;
            lock (lockObject)
            {
                localData = this.data;
            }
            using (PowerTriggersWcfClient serviceClient = WcfClientFactory.OpenWcfClient())
            {
                serviceClient.PutData(localData);
            }
        }

        private void SwitchToServer()
        {
            try
            {
                using (SelectServerName form = new SelectServerName(WcfClientFactory.CurrentServerName))
                {
                    var result = form.ShowDialog(this);
                    if (result == System.Windows.Forms.DialogResult.OK)
                    {
                        Cursor.Current = Cursors.WaitCursor;
                        string oldServiceName = WcfClientFactory.CurrentServerName;
                        bool success;

                        try
                        {
                            WcfClientFactory.CurrentServerName = form.ServerName;
                            if (!ConnectToService())
                            {
                                success = false;
                                Cursor.Current = Cursors.Default;
                                MessageBox.Show(this, "Error connecting to Power Triggers Service so reverting back to previous computer", "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            }
                            else
                            {
                                ShowServerData();
                                success = true;
                            }
                        }
                        catch
                        {
                            success = false;
                            Cursor.Current = Cursors.Default;
                            MessageBox.Show(this, "Error connecting to Power Triggers Service so reverting back to previous computer", "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                        if (!success)
                        {
                            Cursor.Current = Cursors.WaitCursor;
                            WcfClientFactory.CurrentServerName = oldServiceName;
                            ConnectToService();
                            ShowServerData();
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        private void cmdSelect_Click(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                if (!IsConnectedToService(true))
                {
                    ShowServiceNotRunningError();
                }
                else
                {
                    RefreshServiceData();
                    ShowServerData();
                    this.mainProgram.AutoHide = false;
                    if (sender == this.cmdSuspend)
                    {
                        Task[] tasks;
                        Dictionary<TaskTypes, IList<TaskProperty>> defaultProperties;
                        lock (lockObject)
                        {
                            tasks = this.data.SuspendTasks;
                            defaultProperties = this.data.DefaultTaskProperties.ToDictionary(kvp => kvp.Key, kvp => (IList<TaskProperty>)kvp.Value.ToList());
                        }
                        if (this.SelectTasks("Suspend", ref tasks, defaultProperties))
                        {
                            lock (lockObject)
                            {
                                this.data.SuspendTasks = tasks;
                            }
                            this.SaveData();
                            this.ShowServerData();
                        }
                    }
                    else if (sender == this.cmdResume)
                    {
                        Task[] tasks;
                        Dictionary<TaskTypes, IList<TaskProperty>> defaultProperties;
                        lock (lockObject)
                        {
                            tasks = this.data.ResumeTasks;
                            defaultProperties = this.data.DefaultTaskProperties.ToDictionary(kvp => kvp.Key, kvp => (IList<TaskProperty>)kvp.Value.ToList());
                        }
                        if (this.SelectTasks("Resume", ref tasks, defaultProperties))
                        {
                            lock (lockObject)
                            {
                                this.data.ResumeTasks = tasks;
                            }
                            this.SaveData();
                            this.ShowServerData();
                        }
                    }
                    this.mainProgram.AutoHide = true;
                }
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
            }
            finally
            {
                this.mainProgram.AutoHide = true;
                Cursor.Current = Cursors.Default;
            }
        }

        private void frmMain_Deactivate(object sender, EventArgs e)
        {
            if (this.mainProgram.AutoHide)
            {
                this.mainProgram.HideForm();
            }
        }

        private void frmMain_Paint(object sender, PaintEventArgs e)
        {
            Pen pen = SystemPens.ControlLight;
            using (Graphics graphics = base.CreateGraphics())
            {
                graphics.DrawLine(pen, -3, 50, base.ClientSize.Width, 50);
                graphics.DrawLine(pen, -3, this.treeViewEventHistory.Top - 10, base.ClientSize.Width, this.treeViewEventHistory.Top - 10);
            }
        }

        private void ShowServiceNotRunningError()
        {
            Cursor.Current = Cursors.Default;
            MessageBox.Show(this, "Unable to Connect to Power Triggers Service. Check that it is running and then retry this operation", "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void frmMain_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)0x1b) // escape pressed
            {
                e.Handled = true;
                this.mainProgram.HideForm();
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.DateTime.ToString(System.String)"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void openLogToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                if (IsConnectedToService(false))
                {
                    string logText = treeViewEventHistory.ConvertToText();

                    // write to temp file
                    string tempFilePath = System.IO.Path.GetTempPath();
                    string tempFileName = DateTime.Now.ToString("yyMMddHHmmss") + ".log";
                    string fullPath = System.IO.Path.Combine(tempFilePath, tempFileName);
                    if (System.IO.File.Exists(fullPath))
                    {
                        System.IO.File.Delete(fullPath);
                    }
                    System.IO.File.WriteAllText(fullPath, logText);

                    // display temp file in notepad
                    System.Diagnostics.ProcessStartInfo psi = new ProcessStartInfo(fullPath);
                    psi.Verb = "Open";
                    this.mainProgram.AutoHide = false;
                    System.Diagnostics.Process.Start(psi);
                    this.mainProgram.AutoHide = true;
                }
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Windows.Forms.MessageBox.Show(System.Windows.Forms.IWin32Window,System.String)")]
        private void ShowError(Exception ex)
        {
            this.mainProgram.AutoHide = false;
            Cursor.Current = Cursors.Default;
            MessageBox.Show(this, "Error: " + ex.Message, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.Error);
            this.mainProgram.AutoHide = true;
        }

        private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SuspendLayout();
            Size prevSize = this.Size;
            Settings.Default.AlwaysOnTop = !Settings.Default.AlwaysOnTop;
            Settings.Default.Save();
            ShowServerData();

            alwaysOnTopToolStripMenuItem.Checked = Settings.Default.AlwaysOnTop;
            this.TopMost = Settings.Default.AlwaysOnTop;
            this.ControlBox = Settings.Default.AlwaysOnTop;

            Point newLocation = new Point();
            newLocation.Y = this.Top + (int)((prevSize.Height - this.Size.Height) / 2);
            newLocation.X = this.Left + (int)((prevSize.Width - this.Size.Width) / 2);
            this.Location = newLocation;

            this.ResumeLayout();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.mainProgram.ExitApplication();
        }

        private void frmMain_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                this.contextMenuStripForm.Show((Control)sender, new Point(e.X, e.Y), ToolStripDropDownDirection.Default);
            }
        }

        private void frmMain_Activated(object sender, EventArgs e)
        {
            if (this.Visible)
            {
                WriteDebug("frmMain_Activated true");
                if (this.forceNextActivateToAttemptConnection)
                {
                    this.forceNextActivateToAttemptConnection = false;
                    this.Refresh();
                    WcfClientFactory.EnsureWindowsServiceIsRunning(WcfClientFactory.CurrentServerName, this.isFirstRun);
                    this.isFirstRun = false;

                    if (!IsConnectedToService(true))
                    {
                        ShowServiceNotRunningError();
                    }
                }

                this.ShowServerData();
            }
            else
            {
                WriteDebug("frmMain_Activated false");
            }
        }

        private void frmMain_VisibleChanged(object sender, EventArgs e)
        {
            try
            {
                if (this.Visible)
                {
                    // this will force the data to be reloaded during the frmMain_Activated event
                    this.forceNextActivateToAttemptConnection = true;
                    lock (lockObject)
                    {
                        this.data = null;
                    }

                    this.ControlBox = Settings.Default.AlwaysOnTop;

                    Rectangle workingArea = Screen.GetWorkingArea(this);
                    Point newLocation = new Point();
                    newLocation.X = (workingArea.Width - base.Width) - 7;
                    newLocation.Y = (workingArea.Height - base.Height) - 7;
                    this.Location = newLocation;

                    alwaysOnTopToolStripMenuItem.Checked = Settings.Default.AlwaysOnTop;
                    this.TopMost = Settings.Default.AlwaysOnTop;
                }
                else
                {
                    lock (lockObject)
                    {
                        this.data = null;
                    }
                    WcfClientFactory.CloseCallbackClient();
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                }
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
            }
        }

        private bool ConnectToService()
        {
            var savedCursor = Cursor.Current;
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                WcfClientFactory.CloseCallbackClient();

                if (WcfClientFactory.DoesWindowsServiceExist(WcfClientFactory.CurrentServerName))
                {
                    WcfClientFactory.EnsureWindowsServiceIsRunning(WcfClientFactory.CurrentServerName);

                    RefreshServiceData();

                    WcfClientFactory.OpenCallbackClient(treeViewEventHistory);
                }

                return true;
            }
            catch (System.Exception ex)
            {
                EventLogHelper.LogError("ConnectToService() failed", ex);
                return false;
            }
            finally
            {
                Cursor.Current = savedCursor;
            }
        }

        private bool IsConnectedToService(bool forceConnect)
        {
            bool isConnected;

            if (data == null || WcfClientFactory.IsCallbackClientClosed)
            {
                if (forceConnect)
                {
                    isConnected = ConnectToService();
                }
                else
                {
                    isConnected = false;
                }
            }
            else
            {
                isConnected = true;
            }

            return isConnected;
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                this.Hide();
            }
        }

        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            WcfClientFactory.CloseCallbackClient();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "PowerTriggers.EventLogHelper.LogError(System.String,System.Exception)"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "RefreshServiceData")]
        private void RefreshServiceData()
        {
            try
            {
                ConfigData localData;
                using (PowerTriggersWcfClient serviceClient = WcfClientFactory.OpenWcfClient())
                {
                    localData = serviceClient.GetData();
                }
                lock (lockObject)
                {
                    this.data = localData;
                }
            }
            catch (System.Exception ex)
            {
                lock (lockObject)
                {
                    this.data = null;
                }
                EventLogHelper.LogError("RefreshServiceData", ex);
                throw;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        private void WriteDebug(string msg)
        {
#if DEBUG
            Debug.WriteLine(DateTime.Now.ToString() + ": " + msg);
#endif
        }

        private void connectToServertoolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;
                this.mainProgram.AutoHide = false;
                SwitchToServer();
                this.mainProgram.AutoHide = true;
            }
            catch (System.Exception ex)
            {
                ShowError(ex);
                this.mainProgram.AutoHide = true;
            }
            finally
            {
                this.mainProgram.AutoHide = true;
                Cursor.Current = Cursors.Default;
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.mainProgram.AutoHide = true;
            this.Hide();
        }

        private void treeViewEventHistory_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node != null && e.Node is IHistoryItemTreeNode)
            {
                if (e.Node.Nodes.Count == 0)
                {
                    this.mainProgram.AutoHide = false;
                    MessageBox.Show(((IHistoryItemTreeNode)e.Node).HistoryItem.ItemText, "Power Triggers", MessageBoxButtons.OK, MessageBoxIcon.None);
                    this.mainProgram.AutoHide = true;
                }
            }
        }

        // Cancels the resize cursor
        protected override void WndProc(ref Message message)
        {
            const int WM_NCHITTEST = 0x0084;
            switch (message.Msg)
            {
                case WM_NCHITTEST:
                    return;
            }
            base.WndProc(ref message);
        }
    }
}
